/**
 * Created By xiyuan
 * Author server@xiyuan.name (惜缘叛逆)
 * DateTime 2019/3/14 11:25 AM
 * Describe javascript功能描述
 * MIT License http://www.opensource.org/licenses/mit-license.php
 */
const md5 = require('md5')

// 规则正则
const ruleRegexs = {
	text: /^[^\s]*$/,
	hidden: /^[^\s]*$/,
	host: /^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/,
	ip: /^(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})(\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})){3}$/,
	port: /^([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{4}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/,
	path: /^.+$/,
	number: /^\d+$/,
	password: /^.+$/,
	url: /(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?/,
	select: function (value, rule) {
		return {
			value,
			pass: (rule.option || []).findIndex(val => val == value) !== -1
		};
	},
	switch: function (value) {
		return {
			value,
			pass: typeof value === "boolean"
		}
	},
	list_port: function (value, rule, rules, options, isPortMap) {
		let ports = [].concat(value).join(',').split(',');
		const info = [];
		const portList = [];
		// 是否进行端口映射
		isPortMap = isPortMap === undefined ? rule.portMap : isPortMap;
		// 检查是否端口范围段
		if (rule.isRange && (typeof rules.isRange === true || options[rule.isRange])) {
			ports.forEach(port => {
				let ports = port.split('-');
				// 检查是否范围段
				if (ports.length === 2) {
					let start = Number(ports[0]);
					let end = Number(ports[1]);
					
					// 检查起始端口与结束端口是否合法
					if (!ruleRegexs.port.test(String(start)) || !ruleRegexs.port.test(String(end))) {
						return {
							errMsg: "非法的端口"
						}
					}
					
					// 更正顺序
					if (start > end) {
						let _start = start;
						start = end;
						end = _start;
					}
					
					let i = start;
					
					do {
						portList.push(i)
						i++;
					}
					while (i <= end);
					
					info.push({
						isRange: true,
						start,
						end,
						length: Math.abs(start - end)
					})
				} else if (ports.length > 2) {
					return {
						errMsg: "端口范围格式错误"
					}
				} else {
					if (!ruleRegexs.port.test(ports[0])) {
						return {
							errMsg: "非法的端口"
						}
					}
					// 端看
					portList.push(ports[0]);
					info.push({
						port: ports[0]
					})
				}
				
			})
			
			const nowInfo = info;
			
			// 检查是否开启端口映射
			if (isPortMap) {
				// 获取目标映射信息
				let {errMsg, info} = ruleRegexs.list_port(options[rule.portMap], rules[rule.portMap], rules, options, false);
				// 检查是否出错
				if (errMsg) {
					return {
						errMsg: '对应的[' + rule.portMap + ']' + errMsg
					}
				}
				// 对比映射是否有问题
				let pass = nowInfo.every(function (item, index) {
					let targetItem = info[index];
					return item.isRange == targetItem.isRange && item.length === targetItem.length && getType(item.port) === getType(targetItem.port)
				})
				
				if (!pass) {
					return {
						errMsg: '与[' + rule.portMap + ']端口个数不配对'
					}
				}
			}
			
			return {
				info,
				portList,
				pass: true,
				value: ports
			}
			
		} else if (ports.some(port => !ruleRegexs.port.test(port))) {
			return {
				errMsg: "非法的端口"
			}
		} else {
			ports.forEach(port => {
				portList.push(Number(port))
			})
		}
		
		return {
			value: ports.join(','),
			pass: true,
			portList,
		}
	}
	
};

const typeTextMap = {
	text: '文本',
	hidden: '隐藏元素',
	host: '域名',
	ip: 'IP',
	port: '端口',
	path: '文件路径',
	number: '数字',
	select: '选项',
	password: '密码',
	url: 'URL地址',
	switch: '选项',
	list_port: '端口列表'
}

// 选项转换
function optionConvert(name, rules, options, client) {
	// 字段
	let field;
	// 新名称
	let newName = name;
	// 错误信息
	let errMsg;
	let ruleType;
	let ports;
	// 配置数据容器
	const config = {};
	// 配置列表
	const configList = [];
	
	// 结果输出
	function out({type, key} = {}) {
		const res = {
			name,
			ports,
			newName,
			field: key || field,
			ruleType: type || ruleType,
			errMsg,
			configList,
		}
		
		Object.keys(res).forEach(function (key) {
			if (res[key] === undefined) delete res[key];
		})
		
		return res
	}
	
	if (!options) {
		errMsg = '配置为空';
		return out();
	}
	
	// 规则字段集合 (剔除相关特殊key)
	const rulesKeys = Object.keys(rules).filter(field => !(['#'].find(key => field == key)));
	// 移除空值
	Object.keys(options).forEach(key => {
		if (options[key] === null) delete options[key];
	})
	
	while (rulesKeys.length) {
		let key = rulesKeys.shift();
		// 匹配规则，并进行配置规范化
		let {errInfo, type, value, portList} = normalRule(key, rules, options, client);
		
		// 收集端口
		if (portList) ports = portList;
		
		// 检查是否通过
		if (errMsg = errInfo) {
			// 规则类型
			ruleType = type;
			return out({type, key});
		}
		// 临时存储
		if (value !== undefined) config[key] = value;
	}
	
	// 检查特殊描述
	const decorate = rules['#'];
	
	if (decorate) {
		switch (decorate.type) {
			// 范围端口映射
			case 'range':
				// 检查是否开启
				if (options[decorate.field]) {
					newName = 'range:' + name;
				}
				break;
			// 拆分（一般用于负载均衡配置）
			case 'split':
				// 检查是否开启
				let list = options[decorate.field];
				
				list = typeof list === 'string' ? list.split(',') : (list || []);
				list.forEach(function (value) {
					configList.push(Object.assign({}, config, {[decorate.field]: value}))
				})
				return out();
				break;
		}
	}
	configList.push(config)
	return out();
}

// 配置规则常规化
function normalRule(key, rules, options, client) {
	let value = options[key];
	let rule = rules[key];
	// 填写默认值
	if (value === undefined || value === '') {
		if (rule.value !== undefined) {
			value = rule.value;
		} else if (rule.randomValue) {
			value = md5(Date.now() + ':' + Math.random())
		}
	}
	
	// 检查是否函数处理的值
	if (typeof value === "function") {
		value = value(client.name, client)
	}
	// 规则类型
	const type = rule.type;
	let errInfo;
	
	function out({errMsg, ...extendOption} = {}) {
		return {
			value,
			type,
			...extendOption,
			errInfo: errMsg || (errInfo ? '非法的' + errInfo : errInfo),
		}
	}
	
	// 检查是否忽略选项
	if (rule.ignore && options[rule.ignore]) {
		value = undefined
		return out()
	}
	
	// 检查是否必填
	if (rule.required) {
		if ((value === undefined || value === '') && (rule.required === true || options[rule.required] !== undefined)) {
			return out({errMsg: '不能为空'})
		} else if (rule.required !== true && options[rule.required] === undefined) {
			value = undefined
			return out();
		}
	} else if (value === undefined || value === '') {
		return out();
	}
	// 规则校验
	let reg = ruleRegexs[type] || type;
	let nowValue = value;
	// 规则类型检查
	switch (getType(reg)) {
		case 'regexp':
			if (nowValue === undefined || reg.test(String(nowValue))) {
				return out();
			}
			break;
		case 'function':
			let {errMsg, pass, value, portList} = reg(nowValue, rule, rules, options);
			errInfo = errMsg;
			if (pass) {
				return out({portList});
			} else if (errMsg) {
				return out({errMsg})
			}
			break;
	}
	errInfo = typeTextMap [type] || '规则类型';
	return out();
}

// 获取数据类型
function getType(value) {
	return {}.toString.call(value).slice(8, -1).toLocaleLowerCase();
};


module.exports = {
	ruleRegexs,
	typeTextMap,
	normalRule,
	optionConvert
};